﻿using System;

namespace Sgr.Utilities
{
    /// <summary>
    /// 货币操作帮助类
    /// </summary>
    public static class CurrencyConversionHelper
    {
        /// <summary>
        /// 将元转换为分（根据 MidpointRounding.ToEven 四舍五入到分）
        /// </summary>
        /// <param name="yuan">元金额</param>
        /// <returns>分金额</returns>
        /// <exception cref="OverflowException">转换结果超出 Int64 范围时抛出</exception>
        public static long ConvertYuanToCents(decimal yuan)
        {
            // 先乘 100，再按全局舍入模式四舍五入到 0 位小数（即分）
            var centsDecimal = Math.Round(yuan * 100m, 0, MidpointRounding.ToEven);

            if (centsDecimal < long.MinValue || centsDecimal > long.MaxValue)
                throw new OverflowException("金额过大，无法转换为分表示法。");

            return (long)centsDecimal;
        }

        /// <summary>
        /// 将分转换为元（精确到分）
        /// </summary>
        /// <param name="cents">分金额</param>
        /// <returns>元金额</returns>
        public static decimal ConvertCentsToYuan(long cents)
        {
            return cents / 100m;
        }

        /// <summary>
        /// 根据税后价计算税前价
        /// </summary>
        /// <param name="unitPriceWithTax">税后价</param>
        /// <param name="taxRate">税率</param>
        /// <returns>税前价</returns>
        public static decimal CalculateUnitPriceExclTax(decimal unitPriceWithTax, decimal taxRate)
        {
            if (taxRate < 0m || taxRate > 1m)
                throw new ArgumentException("税率必须在0~1之间", nameof(taxRate));

            decimal divisor = 1 + taxRate;
            return unitPriceWithTax / divisor;
        }

        /// <summary>
        /// 根据税前价计算税后价
        /// </summary>
        /// <param name="unitPrice">税后价</param>
        /// <param name="taxRate">税率</param>
        /// <returns>税后价</returns>
        /// <exception cref="ArgumentException"></exception>
        public static decimal CalculateUnitPriceInclTax(decimal unitPrice, decimal taxRate)
        {
            if (taxRate < 0m || taxRate > 1m)
                throw new ArgumentException("税率必须在0~1之间", nameof(taxRate));

            decimal price = unitPrice * (1 + taxRate);
            return price;
        }

        #region 五入金额

        /// <summary>
        /// 四舍五入金额（域内/计算/持久化使用/对外显示）（默认 Internal）
        /// </summary>
        /// <param name="v">待舍入金额</param>
        /// <param name="purpose">用途</param>
        /// <returns>四舍五入后的金额</returns>
        public static decimal CurrencyRound(decimal v, RoundingPurpose purpose = RoundingPurpose.Internal)
        {
            return purpose == RoundingPurpose.Internal
                ? Round(v, 4)    // 内部精度
                : Round(v, 2);   // 显示精度
        }

        /// <summary>
        /// 单价舍入（统一入口，依据用途选择精度）
        /// </summary>
        public static decimal PriceRound(decimal v, RoundingPurpose purpose = RoundingPurpose.Internal)
            => purpose == RoundingPurpose.Internal ? Round(v, 6) : Round(v, 4);

        /// <summary>
        /// 税率舍入（统一入口，依据用途选择精度）
        /// </summary>
        public static decimal TaxRateRound(decimal v, RoundingPurpose purpose = RoundingPurpose.Internal)
            => purpose == RoundingPurpose.Internal ? Round(v, 6) : Round(v, 4);

        /// <summary>
        /// 折扣率舍入（统一入口，依据用途选择精度）
        /// </summary>
        public static decimal DiscountRateRound(decimal v, RoundingPurpose purpose = RoundingPurpose.Internal)
            => purpose == RoundingPurpose.Internal ? Round(v, 6) : Round(v, 4);

        /// <summary>
        /// 汇率舍入（统一入口，依据用途选择精度）
        /// </summary>
        public static decimal ExchangeRateRound(decimal v, RoundingPurpose purpose = RoundingPurpose.Internal)
            => purpose == RoundingPurpose.Internal ? Round(v, 6) : Round(v, 6);

        /// <summary>
        /// 数量舍入（统一入口，依据用途选择精度）
        /// </summary>
        public static decimal QtyRound(decimal v, RoundingPurpose purpose = RoundingPurpose.Internal)
            => purpose == RoundingPurpose.Internal ? Round(v, 6) : Round(v, 6);

        #endregion 五入金额

        /// <summary>
        /// 四舍五入到指定小数位
        /// </summary>
        /// <param name="value"></param>
        /// <param name="decimals"></param>
        /// <param name="mode"></param>
        /// <returns></returns>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        private static decimal Round(decimal value, int decimals, MidpointRounding? mode = null)
        {
            if (decimals < 0 || decimals > 28)
                throw new ArgumentOutOfRangeException(nameof(decimals), "decimals 必须在 0~28 之间");

            return Math.Round(value, decimals, mode ?? MidpointRounding.ToEven);
        }
    }

    /// <summary>
    /// 舍入用途
    /// </summary>
    public enum RoundingPurpose
    {
        /// <summary>
        /// 对外显示
        /// </summary>
        Display,

        /// <summary>
        /// 域内计算与持久化（默认）
        /// </summary>
        Internal
    }
}